#include "testing/testing.hpp"

#include "include/precompiled.hpp"
#include <third_party/LuaJIT/src/lua.hpp>

#include "tbcore/reflection/object.hpp"

struct TestStruct {
	TestStruct() {
		a = 123124;
	}

	~TestStruct() {

	}

	int a;
};

int CreateTestStruct(lua_State* L) {
	return 1;
}

int ReleaseTestStruct(lua_State* L) {
	TestStruct* d = (TestStruct*)lua_touserdata(L, -1);
	//delete d;
	d->~TestStruct();
	//TB_FREE(d);
	return 0;
}

static void *l_alloc(void *ud, void *ptr, size_t osize,
	size_t nsize) {
	(void)ud;  (void)osize;  /* not used */
	if (nsize == 0) {
		free(ptr);
		return NULL;
	}
	else
		return realloc(ptr, nsize);
}

TEST(LuaTypesTest, BasicLuaOperation) {
	int stackSize;

	lua_State *L = lua_newstate(l_alloc, nullptr);
	stackSize = lua_gettop(L);
	int ret = luaL_dostring(L, "a=12");
	stackSize = lua_gettop(L);
	lua_pushstring(L, "asd");
	stackSize = lua_gettop(L);
	const char* str = lua_tostring(L, 1);
	stackSize = lua_gettop(L);
	lua_getglobal(L, "a");
	stackSize = lua_gettop(L);
	lua_Number i = lua_tonumber(L, -1);
	stackSize = lua_gettop(L);
	lua_newtable(L);
	stackSize = lua_gettop(L);
		luaL_newmetatable(L, "testtable");
		lua_pushcfunction(L, ReleaseTestStruct);
		lua_setfield(L, -2, "__gc");
		stackSize = lua_gettop(L);
			lua_pushstring(L, "test table desc");
			stackSize = lua_gettop(L);
			lua_setfield(L, -2, "desc");
			stackSize = lua_gettop(L);
			lua_setmetatable(L, -2);
			stackSize = lua_gettop(L);
			const char* tname = luaL_typename(L, -1);
			stackSize = lua_gettop(L);
	luaL_getmetafield(L, -1, "desc");
	stackSize = lua_gettop(L);
	const char* desc = lua_tostring(L, -1);
	stackSize = lua_gettop(L);

	TestStruct* tests = (TestStruct*)lua_newuserdata(L, sizeof(TestStruct));
	new (tests) TestStruct();
	luaL_newmetatable(L, "testtable");
	lua_setmetatable(L, -2);
	luaL_getmetafield(L, -1, "desc");
	desc = lua_tostring(L, -1);
	lua_pop(L, 1);
	lua_setglobal(L, "tests");

	SUCCEED() << "passed type test";
}

TEST(LuaTypesTest, RegisterObject) {
	lua_State *L = luaL_newstate();

  int statSize = 0;
	
	using namespace TB_NAMESPACE;
	lua::InitializeLuaState(L);

  int ret = luaL_dostring(L, "a = Object.new();");
  if (ret) {
    const char* err = lua_tostring(L, -1);
    ASSERT_EQ(ret, 0) << err;
  }

  statSize = lua_gettop(L);
 	ret = luaL_dostring(L, "b = a");
 	if (ret) {
 		const char* err = lua_tostring(L, -1);
 		ASSERT_EQ(ret, 0) << err;
 	}
 
  statSize = lua_gettop(L);
 	lua_getglobal(L, "b");
  if (Variant* b = (Variant*)lua_touserdata(L, -1)) {
    SUCCEED() << "passed RegisterObject";
  } else {
    FAIL() << "RegisterObject failed";
  }
}

int RegisterGlobalFunction_1(int a, int b) {
  return a - b;
}

TEST(TestLuaTypes, RegisterGlobalFunction) {
  using namespace TB_NAMESPACE;

  REGISTER_GlOBAL_FUNCTION("", RegisterGlobalFunction_1, "int a, int b");

  lua_State *L = luaL_newstate();
	lua::InitializeLuaState(L);

  int ret = luaL_dostring(L, "b = RegisterGlobalFunction_1(1, 3);");
  if (ret) {
    const char* err = lua_tostring(L, -1);
    ASSERT_EQ(ret, 0) << err;
  }

  lua_getglobal(L, "b");
  Variant bvar = lua::GetStackValue(L, -1);
  int bval = bvar.Value<int>();
  EXPECT_EQ(bval, -2);
}

TEST(TestLuaTypes, CreateNObjectAndCallFunction) {
  using namespace TB_NAMESPACE;

  lua_State *L = luaL_newstate();
  lua::InitializeLuaState(L);

  int ret;
  ret = luaL_dostring(L, "a = NObject.new();");
  if (ret) {
    const char* err = lua_tostring(L, -1);
    ASSERT_EQ(ret, 0) << err;
  }

  ret = luaL_dostring(L, "a.Name = \"test_nobject\";");
  if (ret) {
    const char* err = lua_tostring(L, -1);
    ASSERT_EQ(ret, 0) << err;
  }

  ret = luaL_dostring(L, "b = a.Name;");
  if (ret) {
    const char* err = lua_tostring(L, -1);
    ASSERT_EQ(ret, 0) << err;
  }

  lua_getglobal(L, "b");
  Variant bvar = lua::GetStackValue(L, -1);
  std::string bval = bvar.Value<std::string>();
  EXPECT_STREQ(bval.c_str(), "test_nobject");
}